﻿using System;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Windows.Forms;

namespace InstallPartner
{
    /// <summary>
    /// This is the main UI for the Install Partner application.
    /// </summary>
    public partial class MainForm : Form
    {
        #region Private Attributes

        private readonly InstallHelper _installHelper;
        private readonly string _startingProfile;
        private bool _isInitializing = true;
        private bool _closeAfterInstall;
        private DoWorkEventHandler _currentBackgroundWorkerEventHandler;

        #endregion

        #region Constructors

        /// <summary>
        /// Initializes a new instance of the <see cref="MainForm"/> class.
        /// </summary>
        public MainForm() : this(string.Empty) { }

        /// <summary>
        /// Initializes a new instance of the <see cref="MainForm"/> class.
        /// </summary>
        /// <param name="profileName">The name of the profile that is to be automatically installed.</param>
        public MainForm(string profileName)
        {
            InitializeComponent();

            // Create the install helper class
            _installHelper = new InstallHelper();

            // Track the name of the profile to be automatically installed
            _startingProfile = profileName;
        }

        #endregion

        #region Private Methods

        /// <summary>
        /// Gets the specified <see cref="ListViewGroup"/> by name if it exists; otherwise,
        /// a new <see cref="ListViewGroup"/> is created.
        /// </summary>
        /// <param name="listView">The <see cref="ListView"/> control to get the group from.</param>
        /// <param name="title">The group title.</param>
        /// <returns>A <see cref="ListViewGroup"/>.</returns>
        private static ListViewGroup GetListViewGroup(ListView listView, string title)
        {
            foreach (ListViewGroup group in listView.Groups)
            {
                if (group.Name.Equals(title))
                {
                    return group;
                }
            }

            var newGroup = new ListViewGroup(title, title);

            listView.Groups.Add(newGroup);

            return newGroup;
        }

        /// <summary>
        /// Shows the on-line help.
        /// </summary>
        /// <remarks>By default, this is the on-line help hosted on CodePlex.</remarks>
        private static void ShowOnlineHelp()
        {
            Process.Start(Properties.Resources.HelpUrl);
        }

        /// <summary>
        /// Initializes the form.
        /// </summary>
        private void InitializeForm()
        {
            // Databind the profiles drop-down list
            cboProfile.DataSource = _installHelper.Profiles;
            cboProfile.ValueMember = InstallHelper.IdColumn;
            cboProfile.DisplayMember = InstallHelper.NameColumn;

            // Look for the base profile in the same folder as the EXE
            var baseProfilePath = InstallHelper.DefaultConfigurationFileName;

            if (File.Exists(baseProfilePath))
            {
                OpenFile(baseProfilePath);
            }
            else
            {
                // Reload the lists
                RefreshAll();

                _isInitializing = false;

                cboProfile_SelectedIndexChanged(null, null);
            }

            // Check for a starting profile and act accordingly
            if (!string.IsNullOrEmpty(_startingProfile))
            {
                var profileId = _installHelper.GetProfileId(_startingProfile);
                DialogResult result;

                if (profileId > 0)
                {
                    // Load the default profile
                    cboProfile.Text = _startingProfile;

                    using (var dlg = new DelayStartDialog(_startingProfile))
                    {
                        result = dlg.ShowDialog(this);
                    }

                    // If the user did not cancel then go ahead and start the install
                    if (result == DialogResult.OK)
                    {
                        // Set a flag so that the application will be closed once the installation has completed
                        _closeAfterInstall = true;

                        InstallSelectedProfile(true);
                    }
                }
                else
                {
                    // Alert the user to a non-existing profile
                    MessageBox.Show(
                        this,
                        string.Format("The profile '{0}' does not exist in the current configuration.", _startingProfile),
                        "Profile Not Found",
                        MessageBoxButtons.OK,
                        MessageBoxIcon.Warning);
                }
            }

            UpdateOptions();
        }

        /// <summary>
        /// Updates menu and toolbar options based upon the current state of the application.
        /// </summary>
        private void UpdateOptions()
        {
            var isModifiable = !InstallHelper.IsReadOnly;

            // File menu
            mnuFileNew.Enabled = isModifiable;
            mnuFileSave.Enabled = isModifiable && _installHelper.IsDirty;
            mnuFileSaveAs.Enabled = isModifiable;

            // Edit menu
            mnuEditApplicationAdd.Enabled = isModifiable;
            mnuEditApplicationEdit.Enabled = isModifiable && listViewApplications.SelectedItems.Count > 0;
            mnuEditApplicationDelete.Enabled = isModifiable && listViewApplications.SelectedItems.Count > 0;

            mnuEditProfileAdd.Enabled = isModifiable;
            mnuEditProfileDelete.Enabled = isModifiable &&
                                       cboProfile.SelectedIndex >= 0 &&
                                       cboProfile.Text != InstallHelper.BaseProfileName;
            mnuEditProfileCopy.Enabled = isModifiable && cboProfile.SelectedIndex >= 0;

            // Actions menu
            mnuActionsInstallProfile.Enabled = _installHelper.HasSelectedItems;
            mnuActionsInstallSelection.Enabled = !string.IsNullOrEmpty(_installHelper.SelectedApplication);

            // Toolbar
            tsbFileNew.Enabled = mnuFileNew.Enabled;
            tsbFileSave.Enabled = mnuFileSave.Enabled;

            tsbEditApplicationEditAdd.Enabled = mnuEditApplicationAdd.Enabled;
            tsbEditApplicationEdit.Enabled = mnuEditApplicationEdit.Enabled;
            tsbEditApplicationDelete.Enabled = mnuEditApplicationDelete.Enabled;

            tsbEditProfileAdd.Enabled = mnuEditProfileAdd.Enabled;
            tsbEditProfileDelete.Enabled = mnuEditProfileDelete.Enabled;
            tsbEditProfileCopy.Enabled = mnuEditProfileCopy.Enabled;

            tsbActionsInstallProfile.Enabled = mnuActionsInstallProfile.Enabled;
            tsbActionsInstallSelection.Enabled = mnuActionsInstallSelection.Enabled;

            // Form buttons
            btnAddApplication.Enabled = isModifiable && mnuEditApplicationEdit.Enabled;
            btnRemoveApplication.Enabled = isModifiable && listViewSelections.SelectedItems.Count > 0;

            btnMoveUp.Enabled = isModifiable &&
                                listViewSelections.SelectedItems.Count > 0 &&
                                listViewSelections.SelectedIndices[0] > 0;
            btnMoveDown.Enabled = isModifiable &&
                                  listViewSelections.SelectedItems.Count > 0 &&
                                  listViewSelections.SelectedIndices[listViewSelections.SelectedItems.Count - 1] <
                                  listViewSelections.Items.Count - 1;

            // Context menus
            availableAppMenuInstallSelectedProfile.Enabled = mnuActionsInstallProfile.Enabled;
            availableAppMenuInstallSelectedApplication.Enabled = mnuActionsInstallSelection.Enabled;
            availableAppMenuSelectApplication.Enabled = btnAddApplication.Enabled;
            selectedAppMenuInstallSelectedProfile.Enabled = mnuActionsInstallProfile.Enabled;
            selectedAppMenuInstallSelectedApplication.Enabled = mnuActionsInstallSelection.Enabled;
            selectedAppMenuRemoveApplication.Enabled = btnRemoveApplication.Enabled;

            statusLabel.Text = InstallHelper.IsReadOnly
                                   ? string.Format(
                                         "Configuration file currently locked by {0}.",
                                         InstallHelper.LockOwner)
                                   : "Ready";
        }

        /// <summary>
        /// Refreshes all list controls on the form.
        /// </summary>
        private void RefreshAll()
        {
            // Display the file name in the title bar
            Text = string.Format(
                "Install Partner - [{0}]",
                String.IsNullOrEmpty(_installHelper.FileName)
                    ? "<New List>"
                    : _installHelper.FileName);

            RefreshApplicationList();
            RefreshSelectedItemsList();
        }

        /// <summary>
        /// Refreshes the application list.
        /// </summary>
        private void RefreshApplicationList()
        {
            listViewApplications.BeginUpdate();
            listViewApplications.Items.Clear();

            foreach (DataRow row in _installHelper.Applications.Tables[InstallHelper.ApplicationTable].Rows)
            {
                if (row.RowState != DataRowState.Deleted &&
                    !_installHelper.IsApplicationSelected(
                        (int)cboProfile.SelectedValue,
                        row[InstallHelper.NameColumn].ToString()))
                {
                    var item = new ListViewItem(
                        new[] { row[InstallHelper.NameColumn].ToString() },
                        GetListViewGroup(
                            listViewApplications,
                            row[InstallHelper.GroupColumn].ToString()));

                    item.Tag = row;

                    listViewApplications.Items.Add(item);
                }
            }

            listViewApplications.EndUpdate();
        }

        /// <summary>
        /// Refreshes the selected items list.
        /// </summary>
        private void RefreshSelectedItemsList()
        {
            listViewSelections.BeginUpdate();
            listViewSelections.Items.Clear();

            foreach (DataRowView row in _installHelper.SelectedItems)
            {
                var item = new ListViewItem(new[] { row[InstallHelper.ApplicationNameColumn].ToString() });

                listViewSelections.Items.Add(item);
            }

            listViewSelections.EndUpdate();
        }

        /// <summary>
        /// Opens a configuration file.
        /// </summary>
        private void Open()
        {
            var result = DialogResult.No;

            if (_installHelper.IsDirty)
            {
                result = MessageBox.Show(
                    this,
                    "You have unsaved changes.  Would you like to save them now?",
                    "Unsaved Changes",
                    MessageBoxButtons.YesNoCancel,
                    MessageBoxIcon.Question);

                // Save the current changes before proceeding
                if (result == DialogResult.Yes)
                {
                    Save();
                }
            }

            // If the user hasn't cancelled, then go ahead and open a new file
            if (result != DialogResult.Cancel)
            {
                openFileDialog1.CheckPathExists = true;
                openFileDialog1.DefaultExt = ".xml";
                openFileDialog1.Filter = "XML Files (*.xml)|*.xml|All Files (*.*)|*.*";
                openFileDialog1.FilterIndex = 0;
                openFileDialog1.Multiselect = false;
                openFileDialog1.Title = "Open Configuration";

                if (openFileDialog1.ShowDialog(this) != DialogResult.Cancel)
                {
                    OpenFile(openFileDialog1.FileName);
                }
            }

            UpdateOptions();
        }

        /// <summary>
        /// Opens the specified file.
        /// </summary>
        /// <param name="fileName">The name of the file to open.</param>
        private void OpenFile(string fileName)
        {
            _isInitializing = true;
            _installHelper.Load(fileName);
            _isInitializing = false;

            _installHelper.SetProfile((int)cboProfile.SelectedValue);

            RefreshAll();
        }

        /// <summary>
        /// Creates a new, empty configuration file.
        /// </summary>
        private void New()
        {
            var result = DialogResult.No;

            if (_installHelper.IsDirty)
            {
                result = MessageBox.Show(
                    this,
                    "You have unsaved changes.  Would you like to save them now?",
                    "Unsaved Changes",
                    MessageBoxButtons.YesNoCancel,
                    MessageBoxIcon.Question);

                // Save the current changes before proceeding
                if (result == DialogResult.Yes)
                {
                    Save();
                }
            }

            // If the user hasn't cancelled, then go ahead and open a new file
            if (result != DialogResult.Cancel)
            {
                _isInitializing = true;

                _installHelper.Load(string.Empty);

                _isInitializing = false;

                _installHelper.SetProfile((int)cboProfile.SelectedValue);
                RefreshAll();
            }

            UpdateOptions();
        }

        /// <summary>
        /// Saves the current configuration.
        /// </summary>
        private void Save()
        {
            if (_installHelper.IsDirty)
            {
                // Prompt for a filename if one does not already exist
                if (string.IsNullOrEmpty(_installHelper.FileName))
                {
                    SaveAs();
                }
                else
                {
                    _installHelper.Save();
                }
            }

            UpdateOptions();
        }

        /// <summary>
        /// Saves the current configuration to a new file.
        /// </summary>
        private void SaveAs()
        {
            saveFileDialog1.DefaultExt = ".xml";
            saveFileDialog1.Filter = "XML Files (*.xml)|*.xml|All Files (*.*)|*.*";
            saveFileDialog1.FilterIndex = 0;
            saveFileDialog1.Title = "Save Configuration";

            if (saveFileDialog1.ShowDialog(this) != DialogResult.Cancel)
            {
                _installHelper.FileName = saveFileDialog1.FileName;
                _installHelper.Save();

                // Display the file name in the title bar
                Text = string.Format(
                    "Install Partner - [{0}]",
                    String.IsNullOrEmpty(_installHelper.FileName)
                        ? "<New List>"
                        : _installHelper.FileName);
            }

            UpdateOptions();
        }

        /// <summary>
        /// Adds a new profile.
        /// </summary>
        private void AddProfile()
        {
            using (var dlg = new AddProfileDialog(_installHelper))
            {
                if (dlg.ShowDialog(this) != DialogResult.Cancel)
                {
                    if (_installHelper.AddProfile(dlg.ProfileName) > 0)
                    {
                        cboProfile.Text = dlg.ProfileName;
                    }

                    UpdateOptions();
                }
            }
        }

        /// <summary>
        /// Copies the selected profile.
        /// </summary>
        private void CopyProfile()
        {
            using (var dlg = new CopyProfileDialog(cboProfile.Text, _installHelper))
            {
                if (dlg.ShowDialog(this) != DialogResult.Cancel)
                {
                    if (_installHelper.CopyProfile(cboProfile.Text, dlg.ProfileName) > 0)
                    {
                        cboProfile.Text = dlg.ProfileName;
                    }

                    UpdateOptions();
                }
            }
        }

        /// <summary>
        /// Deletes the selected profile.
        /// </summary>
        private void DeleteProfile()
        {
            if ((int)cboProfile.SelectedValue == 1)
            {
                MessageBox.Show(
                    this,
                    "The default profile cannot be deleted.",
                    "Delete Profile",
                    MessageBoxButtons.OK,
                    MessageBoxIcon.Warning);
            }
            else
            {
                if (MessageBox.Show(
                    this,
                    string.Format("Delete the profile '{0}'?", cboProfile.Text),
                    "Delete Profile",
                    MessageBoxButtons.YesNo,
                    MessageBoxIcon.Question) == DialogResult.Yes)
                {
                    _installHelper.DeleteProfile((int)cboProfile.SelectedValue);
                    RefreshSelectedItemsList();

                    UpdateOptions();
                }
            }
        }

        /// <summary>
        /// Adds a new application to the list.
        /// </summary>
        private void AddApplication()
        {
            using (var dlg = new AddApplicationDialog())
            {
                if (dlg.ShowDialog(this) != DialogResult.Cancel)
                {
                    _installHelper.AddApplication(
                        dlg.ApplicationGroup,
                        dlg.ApplicationName,
                        dlg.CommandLine,
                        dlg.Arguments);

                    // Reload the application list
                    RefreshApplicationList();
                }
            }

            UpdateOptions();
        }

        /// <summary>
        /// Modifies the selected application.
        /// </summary>
        private void EditApplication()
        {
            if (listViewApplications.SelectedItems.Count > 0)
            {
                using (var dlg = new AddApplicationDialog())
                {
                    var row = (DataRow)listViewApplications.SelectedItems[0].Tag;

                    dlg.ApplicationGroup = row["Group"].ToString();
                    dlg.ApplicationName = row["Name"].ToString();
                    dlg.CommandLine = row["Command"].ToString();
                    dlg.Arguments = row["Arguments"].ToString();

                    if (dlg.ShowDialog(this) != DialogResult.Cancel)
                    {
                        _installHelper.ModifyApplication(
                            (int)row["ID"],
                            dlg.ApplicationGroup,
                            dlg.ApplicationName,
                            dlg.CommandLine,
                            dlg.Arguments);

                        // Reload the application list
                        RefreshApplicationList();

                        UpdateOptions();
                    }
                }
            }
        }

        /// <summary>
        /// Deletes the selected application.
        /// </summary>
        private void DeleteApplication()
        {
            if (listViewApplications.SelectedItems.Count > 0)
            {
                if (MessageBox.Show(
                    this,
                    string.Format("Delete the application '{0}'?", listViewApplications.SelectedItems[0].Text),
                    "Delete Application",
                    MessageBoxButtons.YesNo,
                    MessageBoxIcon.Question) == DialogResult.Yes)
                {
                    foreach (ListViewItem item in listViewApplications.SelectedItems)
                    {
                        _installHelper.DeleteApplication(item.Text);
                    }

                    RefreshAll();
                }
            }

            UpdateOptions();
        }

        /// <summary>
        /// Installs all applications specified in the selected profile.
        /// </summary>
        /// <param name="skipPrompt">Specifies whether the confirmation prompt should be skipped.</param>
        private void InstallSelectedProfile(bool skipPrompt)
        {
            if (skipPrompt || (listViewSelections.Items.Count > 0 &&
                MessageBox.Show(
                this,
                string.Format("Install all applications in the profile '{0}'?", cboProfile.Text),
                "Install Selected Profile",
                MessageBoxButtons.YesNo,
                MessageBoxIcon.Question) == DialogResult.Yes))
            {
                // Set tab page to "Install Progress" tab
                tabControl1.SelectedIndex = 1;
                btnCancel.Enabled = true;

                // Initialize the progress bar
                progressBar.Minimum = 0;
                progressBar.Maximum = 100;
                progressBar.Value = 0;

                lblStatus.Text = "Running...";
                txtLog.Text = string.Format(
                    "Install Partner v{0}\r\n\r\n>>> Started: {1}\r\n",
                    Assembly.GetExecutingAssembly().GetName().Version,
                    DateTime.Now);

                // Setup background work event handler
                _currentBackgroundWorkerEventHandler = bgwInstall_DoWorkProfile;
                bgwInstall.DoWork += _currentBackgroundWorkerEventHandler;

                // Start the background thread
                bgwInstall.RunWorkerAsync();
            }
        }

        /// <summary>
        /// Installs the currently selected applications.
        /// </summary>
        private void InstallSelectedApplication()
        {
            if (!string.IsNullOrEmpty(_installHelper.SelectedApplication) &&
                MessageBox.Show(
                this,
                string.Format("Install '{0}'?", _installHelper.SelectedApplication),
                "Install Selected Application",
                MessageBoxButtons.YesNo,
                MessageBoxIcon.Question) == DialogResult.Yes)
            {
                // Set tab page to "Install Progress" tab
                tabControl1.SelectedIndex = 1;
                btnCancel.Enabled = true;

                // Initialize the progress bar
                progressBar.Minimum = 0;
                progressBar.Maximum = 100;
                progressBar.Value = 0;

                lblStatus.Text = "Running...";
                txtLog.Text = string.Format(
                    "Install Partner v{0}\r\n\r\n>>> Started: {1}\r\n",
                    Assembly.GetExecutingAssembly().GetName().Version,
                    DateTime.Now);

                // Setup background work event handler
                _currentBackgroundWorkerEventHandler = bgwInstall_DoWorkSelection;
                bgwInstall.DoWork += _currentBackgroundWorkerEventHandler;

                // Start the background thread
                bgwInstall.RunWorkerAsync();
            }
        }

        /// <summary>
        /// Shows the about dialog.
        /// </summary>
        private void ShowAboutDialog()
        {
            using (var dlg = new AboutDialog())
            {
                dlg.ShowDialog(this);
            }
        }

        #endregion

        #region Event Handlers

        /// <summary>
        /// Handles the Load event of the MainForm control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void MainForm_Load(object sender, EventArgs e)
        {
            InitializeForm();
        }

        /// <summary>
        /// Handles the FormClosing event of the MainForm control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.Windows.Forms.FormClosingEventArgs"/> instance containing the event data.</param>
        private void MainForm_FormClosing(object sender, FormClosingEventArgs e)
        {
            var result = DialogResult.No;

            if (_installHelper.IsDirty)
            {
                result = MessageBox.Show(
                    this,
                    "You have unsaved changes.  Would you like to save them now?",
                    "Unsaved Changes",
                    MessageBoxButtons.YesNoCancel,
                    MessageBoxIcon.Question);

                // Save the current changes before proceeding
                if (result == DialogResult.Yes)
                {
                    Save();
                }
            }

            // If the user hasn't cancelled, then go ahead and open a new file
            if (result == DialogResult.Cancel)
            {
                e.Cancel = true;
            }
        }

        /// <summary>
        /// Handles the FormClosed event of the MainForm control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.Windows.Forms.FormClosedEventArgs"/> instance containing the event data.</param>
        private void MainForm_FormClosed(object sender, FormClosedEventArgs e)
        {
            InstallHelper.RemoveFileLock(_installHelper.FileName);
        }

        /// <summary>
        /// Handles the Resize event of the MainForm control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void MainForm_Resize(object sender, EventArgs e)
        {
            // Resize the ListView columns to avoid horizontal scrollbars
            listViewApplications.Columns[0].Width = listViewApplications.Width - 22;
            listViewSelections.Columns[0].Width = listViewSelections.Width - 22;
        }

        /// <summary>
        /// Handles the Click event of the mnuFileOpen control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void mnuFileOpen_Click(object sender, EventArgs e)
        {
            Open();
        }

        /// <summary>
        /// Handles the Click event of the mnuFileNew control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void mnuFileNew_Click(object sender, EventArgs e)
        {
            New();
        }

        /// <summary>
        /// Handles the Click event of the mnuFileSave control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void mnuFileSave_Click(object sender, EventArgs e)
        {
            Save();
        }

        /// <summary>
        /// Handles the Click event of the mnuFileSaveAs control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void mnuFileSaveAs_Click(object sender, EventArgs e)
        {
            SaveAs();
        }

        /// <summary>
        /// Handles the Click event of the mnuFileExit control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void mnuFileExit_Click(object sender, EventArgs e)
        {
            Close();
        }

        /// <summary>
        /// Handles the Click event of the mnuActionsInstallProfile control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void mnuActionsInstallProfile_Click(object sender, EventArgs e)
        {
            InstallSelectedProfile(false);
        }

        private void mnuActionsInstallSelection_Click(object sender, EventArgs e)
        {
            InstallSelectedApplication();
        }

        /// <summary>
        /// Handles the Click event of the mnuEditApplicationAdd control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void mnuEditApplicationAdd_Click(object sender, EventArgs e)
        {
            AddApplication();
        }

        /// <summary>
        /// Handles the Click event of the mnuEditApplicationEdit control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void mnuEditApplicationEdit_Click(object sender, EventArgs e)
        {
            EditApplication();
        }

        /// <summary>
        /// Handles the Click event of the mnuEditApplicationDelete control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void mnuEditApplicationDelete_Click(object sender, EventArgs e)
        {
            DeleteApplication();
        }

        /// <summary>
        /// Handles the Click event of the mnuEditProfileAdd control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void mnuEditProfileAdd_Click(object sender, EventArgs e)
        {
            AddProfile();
        }

        /// <summary>
        /// Handles the Click event of the mnuEditProfileCopy control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void mnuEditProfileCopy_Click(object sender, EventArgs e)
        {
            CopyProfile();
        }

        /// <summary>
        /// Handles the Click event of the mnuEditProfileDelete control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void mnuEditProfileDelete_Click(object sender, EventArgs e)
        {
            DeleteProfile();
        }

        /// <summary>
        /// Handles the Click event of the mnuHelpOnlineHelp control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void mnuHelpOnlineHelp_Click(object sender, EventArgs e)
        {
            ShowOnlineHelp();
        }

        /// <summary>
        /// Handles the Click event of the mnuHelpAbout control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void mnuHelpAbout_Click(object sender, EventArgs e)
        {
            ShowAboutDialog();
        }

        /// <summary>
        /// Handles the Opening event of the availableAppMenu control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.ComponentModel.CancelEventArgs"/> instance containing the event data.</param>
        private void ContextMenu_Opening(object sender, CancelEventArgs e)
        {
            UpdateOptions();
        }

        /// <summary>
        /// Handles the Click event of the tsbSortItems control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void tsbSortItems_Click(object sender, EventArgs e)
        {
            tsbGroupItems.Checked = !tsbSortItems.Checked;
            listViewApplications.ShowGroups = tsbGroupItems.Checked;
        }

        /// <summary>
        /// Handles the Click event of the tsbGroupItems control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void tsbGroupItems_Click(object sender, EventArgs e)
        {
            tsbSortItems.Checked = !tsbGroupItems.Checked;
            listViewApplications.ShowGroups = tsbGroupItems.Checked;
        }

        /// <summary>
        /// Handles the SelectedIndexChanged event of the cboProfile control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void cboProfile_SelectedIndexChanged(object sender, EventArgs e)
        {
            if (!_isInitializing)
            {
                _installHelper.SetProfile((int)cboProfile.SelectedValue);

                RefreshAll();
                UpdateOptions();
            }
        }

        /// <summary>
        /// Handles the Click event of the btnAdd control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void btnAddApplication_Click(object sender, EventArgs e)
        {
            foreach (ListViewItem item in listViewApplications.SelectedItems)
            {
                _installHelper.AddSelection((int)cboProfile.SelectedValue, item.Text);
            }

            RefreshAll();
            UpdateOptions();
        }

        /// <summary>
        /// Handles the Click event of the btnRemove control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void btnRemoveApplication_Click(object sender, EventArgs e)
        {
            foreach (ListViewItem item in listViewSelections.SelectedItems)
            {
                _installHelper.RemoveSelection((int)cboProfile.SelectedValue, item.Text);
            }

            RefreshAll();
            UpdateOptions();
        }

        /// <summary>
        /// Handles the Click event of the btnMoveUp control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void btnMoveUp_Click(object sender, EventArgs e)
        {
            var oldIndex = listViewSelections.SelectedIndices[0];

            if (oldIndex > 0)
            {
                foreach (ListViewItem item in listViewSelections.SelectedItems)
                {
                    _installHelper.MoveSelectionUp((int)cboProfile.SelectedValue, item.Text);
                }

                RefreshSelectedItemsList();

                listViewSelections.Items[oldIndex - 1].Selected = true;
            }

            UpdateOptions();
        }

        /// <summary>
        /// Handles the Click event of the btnMoveDown control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void btnMoveDown_Click(object sender, EventArgs e)
        {
            var oldIndex = listViewSelections.SelectedIndices[0];

            if (oldIndex < listViewSelections.Items.Count - 1)
            {
                foreach (ListViewItem item in listViewSelections.SelectedItems)
                {
                    _installHelper.MoveSelectionDown((int)cboProfile.SelectedValue, item.Text);
                }

                RefreshSelectedItemsList();

                listViewSelections.Items[oldIndex + 1].Selected = true;
            }

            UpdateOptions();
        }

        /// <summary>
        /// Handles the Click event of the btnCancel control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void btnCancel_Click(object sender, EventArgs e)
        {
            var result = MessageBox.Show(
                this,
                "Do you want to stop the currently running process?",
                "Cancel",
                MessageBoxButtons.YesNoCancel,
                MessageBoxIcon.Question);

            if (result == DialogResult.Yes)
            {
                _installHelper.CancelProcess();
            }

            if (result != DialogResult.Cancel)
            {
                bgwInstall.CancelAsync();
            }
        }

        /// <summary>
        /// Handles the DoWork event of the bgwInstall control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.ComponentModel.DoWorkEventArgs"/> instance containing the event data.</param>
        private void bgwInstall_DoWorkProfile(object sender, DoWorkEventArgs e)
        {
            var count = 0;
            var scale = 100 / listViewSelections.Items.Count;
            var status = new InstallStatus();

            foreach (DataRowView item in _installHelper.SelectedItems)
            {
                // Get application information
                var row = _installHelper.GetApplicationRow(item[InstallHelper.ApplicationNameColumn].ToString());
                var command = row[InstallHelper.CommandColumn].ToString();
                var arguments = row[InstallHelper.ArgumentsColumn].ToString();

                status.StatusLine = string.Format("Installing {0}...", item[InstallHelper.ApplicationNameColumn]);
                status.StatusLog = string.Format("[{0}] Installing {1}...", DateTime.Now, item[InstallHelper.ApplicationNameColumn]);

                // Update the status displayed on the UI (in a thread-safe manner)
                bgwInstall.ReportProgress(count * scale, status);

                // Kick off the install and store the results
                status.StatusLog = _installHelper.RunProcess(command, arguments);

                // Update the status displayed on the UI (in a thread-safe manner)
                count++;
                bgwInstall.ReportProgress(count * scale, status);

                // Exit the loop if a cancellation is pending
                if (bgwInstall.CancellationPending)
                {
                    e.Cancel = true;
                    break;
                }
            }
        }

        /// <summary>
        /// Handles the DoWork event of the bgwInstall control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.ComponentModel.DoWorkEventArgs"/> instance containing the event data.</param>
        private void bgwInstall_DoWorkSelection(object sender, DoWorkEventArgs e)
        {
            var count = 0;
            var scale = 100;
            var status = new InstallStatus();

            // Get application information
            var row = _installHelper.GetApplicationRow(_installHelper.SelectedApplication);
            var command = row[InstallHelper.CommandColumn].ToString();
            var arguments = row[InstallHelper.ArgumentsColumn].ToString();

            status.StatusLine = string.Format("Installing {0}...", _installHelper.SelectedApplication);
            status.StatusLog = string.Format("[{0}] Installing {1}...", DateTime.Now, _installHelper.SelectedApplication);

            // Update the status displayed on the UI (in a thread-safe manner)
            bgwInstall.ReportProgress(count * scale, status);

            // Kick off the install and store the results
            status.StatusLog = _installHelper.RunProcess(command, arguments);

            // Update the status displayed on the UI (in a thread-safe manner)
            count++;
            bgwInstall.ReportProgress(count * scale, status);

            // Exit the loop if a cancellation is pending
            if (bgwInstall.CancellationPending)
            {
                e.Cancel = true;
            }
        }

        /// <summary>
        /// Handles the ProgressChanged event of the bgwInstall control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.ComponentModel.ProgressChangedEventArgs"/> instance containing the event data.</param>
        private void bgwInstall_ProgressChanged(object sender, ProgressChangedEventArgs e)
        {
            var status = (InstallStatus)e.UserState;

            progressBar.Value = e.ProgressPercentage;

            if (status.StatusLine != null)
            {
                lblStatus.Text = status.StatusLine;
            }

            if (status.StatusLog != null)
            {
                txtLog.Text += string.Format("\r\n{0}", status.StatusLog);
            }

            // Set focus to end of text
            txtLog.SelectionLength = 0;
            txtLog.SelectionStart = txtLog.Text.Length;
            txtLog.ScrollToCaret();
        }

        /// <summary>
        /// Handles the RunWorkerCompleted event of the bgwInstall control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.ComponentModel.RunWorkerCompletedEventArgs"/> instance containing the event data.</param>
        private void bgwInstall_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            // Clear the background worker event handler
            bgwInstall.DoWork -= _currentBackgroundWorkerEventHandler;

            // Update the status
            lblStatus.Text = e.Cancelled ? "Cancelled" : "Done";
            txtLog.Text += "\r\n" + lblStatus.Text;
            txtLog.Text += string.Format("\r\n\r\n>>> Finished: {0}\r\n", DateTime.Now);
            txtLog.SelectionLength = 0;
            txtLog.SelectionStart = txtLog.Text.Length;
            txtLog.ScrollToCaret();

            // Write the log file out to the root folder
            InstallHelper.SaveLog(txtLog.Text);

            btnCancel.Enabled = false;

            // If the install was not manually cancelled and it was kicked off automatically
            // (e.g. via command line) then close the application
            if (!e.Cancelled && _closeAfterInstall)
            {
                Close();
            }
        }

        /// <summary>
        /// Handles the SelectedIndexChanged event of the listViewApplications control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void ControllActionOccurred(object sender, EventArgs e)
        {
            UpdateOptions();
        }

        /// <summary>
        /// Handles the ItemSelectionChanged event of the listViewApplications control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.Windows.Forms.ListViewItemSelectionChangedEventArgs"/> instance containing the event data.</param>
        private void listViewApplications_ItemSelectionChanged(object sender, ListViewItemSelectionChangedEventArgs e)
        {
            var listView = (ListView)sender;

            _installHelper.SelectedApplication = listView.SelectedItems.Count > 0
                                                     ? listView.SelectedItems[0].Text
                                                     : null;
        }

        /// <summary>
        /// Handles the Enter event of the listViewApplications control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void listViewApplications_Enter(object sender, EventArgs e)
        {
            var listView = (ListView)sender;

            _installHelper.SelectedApplication = listView.SelectedItems.Count > 0
                                                     ? listView.SelectedItems[0].Text
                                                     : null;

            UpdateOptions();
        }

        #endregion
    }
}
